Introduction + Methods
This is the 3rd time I’m trying to estimate the needed sample size
for our study. At this point, you should (hopefully) know the
drill…
This time we will try to estimate our needed N with another simulation
approach using deviance to a “true score” which we will get with
sampling a lot.
We use this sample sequence:
sample_sequence
## [1] 20 30 40 50 60 70 80 90 100 150 200 250 500 750 1000
## [16] 1250 1500 1750 2000 2250 2500 2750 3000 5000
And for each of these sample sizes, we simulate 400
times (while higher numbers would lead to a more robust/ reliable
finding, I still need to make sure that it can still be computable)…
Here are our assumed population distribution, which we will sample
from again:
normal <- round(rnorm(n_population, mean = 9,sd = 1.2))
normal <- pmin(pmax(normal, min_likert), max_likert)
strong_pol <- round(rbeta(n_population, shape1 = .1, shape2 = .1)*10) +1
strong_pol <- pmin(pmax(strong_pol, min_likert), max_likert)
rare <- c(round(rnorm(n_population *(1- prop_minority), mean = 2,sd = 1.3)),
round(rnorm(n_population *prop_minority, mean = 11,sd = .5)))
rare <- pmin(pmax(rare, min_likert), max_likert)
normal_2 <- round(rnorm(n_population, mean = 8,sd = 1))
normal_2 <- pmin(pmax(normal_2, min_likert), max_likert)
Pop_df <- data.frame(`Normal Distribution`= normal,
`Normal Distribution 2` = normal_2,
`Rare Polarization` = rare,
`Strong Polarization` = strong_pol)
names(Pop_df) <- c("None", "None 2", "Rare", "Strong Pol.")
Pop_df_plot <- Pop_df %>% pivot_longer(everything(), values_to = "Rating", names_to = "Distr") %>%
mutate(Distr = factor(Distr, levels = c("None", "None 2","Rare", "Strong Pol.")))
Pop_df_plot %>% ggplot(aes(Rating, fill = Distr))+
geom_bar(width= .75)+
facet_grid(rows = vars(Distr))+
scale_x_continuous(n.breaks = 11, expand = c(0,0))+
theme_minimal()+
theme(strip.text = element_blank(),
axis.text.y = element_blank(),
legend.position = "none",
plot.margin = margin(1, 2, 0, 0),
axis.text.x = element_text(size = 12))+
geom_text(aes(x=6,y = 3500, label= Distr, color = Distr), size = 6.8)+
scale_fill_manual(values = colors)+
scale_color_manual(values = colors)+
ylab("Count")

# max_cols <- max(sampled)
#
# # Register parallel backend with the desired number of cores
# num_cores <- detectCores()-1
#
# cl <- makeCluster(num_cores)
# registerDoParallel(cl)
#
# #rotate Pop_df, so our function works (each row needs to be a different distribution, instead of each col)
# rot_Pop_df <- as.data.frame(t(Pop_df))
#
# risk_dist_tr <- data.frame(risk_distribution = 1:4,
# risk_dist_transl = rownames(rot_Pop_df))
#
# # Define function to process each combination of risk_distribution, samplesize and replications per setting
# sample_and_replicate_for_all_risks <- function(i) {
# sampled_matrix_list <- list()
#
# for (j in 1:nrow(rot_Pop_df)) {
# mat <- replicate(replications_per_setting,
# sample(1:n_population, size = sampled[i, 1], replace = TRUE)) #create matrix of our samples with replacing, times n - replications
#
# sampled_table <- rot_Pop_df[j, mat] #using the matrix, collect the values from our risk distribution matrix (as a vector though...)
# sampled_matrix <- as.data.frame(matrix(sampled_table,
# nrow = replications_per_setting,
# ncol = sampled[i, 1],
# byrow = TRUE,
# dimnames = NULL)) #create df out of these vectors instead of flat vectors
#
#
# if (ncol(sampled_matrix) < max_cols) {
# padding_matrix <- matrix(NA,
# nrow = nrow(sampled_matrix),
# ncol = max_cols - ncol(sampled_matrix))
# sampled_matrix <- cbind(sampled_matrix, padding_matrix)
# } #if matrix is not wide enough for our end result matrix, padd it with NA columns, so binding rows is doable (needs same amount of ncols)
#
# sampled_matrix <- cbind(matrix(sampled[i,1], nrow = replications_per_setting),
# matrix(j, nrow = replications_per_setting),
# sampled_matrix) #bind columns with additional information such as sample size and which risk_distribution was sampled
#
# colnames(sampled_matrix) <- c("sample_size", "risk_distribution", paste0("rating_", 1:max_cols)) #rewrite colnames so it is identical to the bigh matrix
# sampled_matrix_list[[j]] <- sampled_matrix #store in list
# }
# return(do.call(rbind, sampled_matrix_list)) #after all risk distributions are sampled from, bind them all and return the output
#
# }
#
# # Perform parallel processing using foreach, iterating through the different samplesizes
# result <- foreach(i = 1:nrow(sampled), .combine = rbind) %dopar% {
# sample_and_replicate_for_all_risks(i)
# }
#
# # Stop the parallel backend
# stopCluster(cl)
#
# result <- mutate_all(result, as.numeric)
#
#
# vis_miss(result[,seq(from =3, to = ncol(result), length.out =20)], show_perc_col = F)
#taken from my previous work
# calc_bimodality_coefficient <- function(vec){
# skew <- skew(vec, na.rm = TRUE, type = 3)
# kurtosis <- kurtosi(vec, na.rm = TRUE, type = 3)
# n <- sum(!is.na(vec))
# return((skew^2+1) / (kurtosis + ((3*((n-1))^2)/((n-2)*(n-3))) ))
# }
#
# # Created this one "from scratch", as we work with a smaller scale of 11 instead of 101, the calculation of polarization does not take too long anymore
#
# calc_polarization <- function(vec){
# vec2 <- as.vector(vec)
# freq_vec <- agrmt::collapse(vec2, pos = c(1:11))
# return(agrmt::polarization(freq_vec))
# }
#
# # this code is commented, as the computation takes too long, and I hate waiting while kniting. I have saved a sepparate rds file, which will be read in instead. Readers who want to uncomment section, select the lines to uncomment and press Ctrl + Shift + C (on Windows/Linux) or Cmd + Shift + C (on macOS)
# #
# # #apply the functions to our result matrix, therefore calculate for each drawn sample the polarization metrics
# BC_result <- apply(result[,-c(1:2)], 1, calc_bimodality_coefficient)
# sum(is.na(BC_result))
#
#
# # Register parallel backend with the desired number of cores
# num_cores <- detectCores()-1
#
# cl <- makeCluster(num_cores)
# registerDoParallel(cl)
#
# # Perform parallel processing using foreach, iterating through the different drawn samples
# polarization_result <- foreach(i = 1:nrow(result), .combine = rbind) %dopar% {
# calc_polarization(result[i, -c(1:2)])
# }
# sum(is.na(polarization_result))
#
# # Stop the parallel backend
# stopCluster(cl)
#
#
# combined_result_measures <- cbind(polarization_result,
# BC_result,
# result[,1:2]
# )
# sum(is.na(combined_result_measures))
#
#
# combined_result_measures <- combined_result_measures %>%
# left_join(risk_dist_tr, by = "risk_distribution") %>%
# mutate(fsample_size = factor(sample_size, ordered = T))
#
# saveRDS(combined_result_measures, "saved_RDS\\combined_result_measures_power_analysis_deviance.rds")
combined_result_measures <- readRDS("saved_RDS\\combined_result_measures_power_analysis_deviance.rds")
vis_miss(combined_result_measures, sort_miss = F)

Results
In the following density plots, we see the distribution of each
calculated polarization measure (from the book
“Triggerpunkte” from Mau et al.), which ranges from 0 (not polarized,
people are in agreement) to 1 (highest polarization). We can see that
the more we sample, the more the results converge to a “true” score for
said assumed population distribution.
combined_result_measures %>%
ggplot(aes(polarization_result))+
geom_density(aes(fill = fsample_size), alpha = .4)+
facet_grid(rows =vars(risk_dist_transl))

And for the bimodality Coefficient as well:
combined_result_measures %>%
ggplot(aes(BC_result))+
geom_density(aes(fill = fsample_size), alpha = .4)+
facet_grid(rows =vars(risk_dist_transl))

In the following, we will calculate the mode of each
density plot (that is, where the polarization measures converges or has
a high probability that it ends up on X, if we were to sample it
fewer times). We can then plot these findings, and should look
at the point where the modes converges towards the
“true score” (in our case, the sample size of 5000).
combined_result_measures <- combined_result_measures %>%
mutate(grouped_distXsample = paste(risk_dist_transl, fsample_size, sep = "_"))
splitted <- split(combined_result_measures$polarization_result, f = combined_result_measures$grouped_distXsample)
splitted2 <- split(combined_result_measures$BC_result, f = combined_result_measures$grouped_distXsample)
mode_dens_res <- data.frame()
for (i in 1:length(splitted)) {
dens_res <- density(splitted[[i]])
dens_res2 <- density(splitted2[[i]])
dens_df <- data.frame(group = names(splitted)[i],
mode_dens_pol = dens_res$x[which.max(dens_res$y)],
mode_dens_BC = dens_res2$x[which.max(dens_res2$y)])
mode_dens_res <- rbind(mode_dens_res, dens_df)
}
cleaned_mode_dens_res <- mode_dens_res %>%
separate(group, sep = "_", into = c("Distribution", "fsample_size")) %>%
mutate(Sample_Size = as.integer(fsample_size)) %>%
pivot_longer(cols = starts_with("mode_dens"),
names_prefix = "mode_dens_",
names_to = "measure",
values_to = "value")
cleaned_mode_dens_res %>%
ggplot(aes(Sample_Size, value, group = Distribution, col = Distribution))+
geom_line(size= 1.1)+
facet_grid(rows = vars(measure))+
scale_x_continuous(breaks = sample_sequence)+
theme_minimal()+
scale_color_manual(values = colors)

Probability of Sample being in True
Score Interval
While the mode could a be good indicator, one might also argue that
it is more relevant to have a good chance of capturing the true
polarization value. That is, the probability that the measure is only
off by only .1 (meaning it is always within a interval of + 0.05 and -
0.05) from the true score. So let’s calculate the % where the
400 simulations per sample size was within this interval.
One could then interpret the results as: the probability of
coming to the same polarization value (with an interval of .1) compared
to if we had sampled 5’000 people.
Interval of .1
# calculate the "true score" value of said polarization measure for each population distribution
true_score <- combined_result_measures %>%
filter(sample_size == 5000) %>%
group_by(risk_dist_transl) %>%
summarize(mean_true_score_BC = mean(BC_result),
mean_true_score_pol = mean(polarization_result))
combined_result_measures_within_interval <- combined_result_measures %>%
left_join(true_score, by = c("risk_dist_transl")) %>%
mutate(within_BC = if_else(BC_result >= mean_true_score_BC- lower_and_upper_interval_limit &
BC_result <= mean_true_score_BC + lower_and_upper_interval_limit, 1, 0),
within_pol = if_else(polarization_result >= mean_true_score_pol- lower_and_upper_interval_limit &
polarization_result <= mean_true_score_pol + lower_and_upper_interval_limit, 1, 0))
summarized_within_interval <-
combined_result_measures_within_interval %>%
group_by(risk_dist_transl, sample_size) %>%
summarize(count_within_BC = sum(within_BC),
count_within_pol = sum(within_pol),
perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
summarized_within_interval %>% ggplot(aes(factor(sample_size), percentage, col = risk_dist_transl, group = risk_dist_transl))+
geom_point()+
geom_line()+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.05 interval of true score",
x = "sample size",
title = "Interval of +- 0.05")+
scale_color_manual(values = colors)+
theme(legend.position = "none")

Interval of .05
As the interval of .05 (which results of a coverage of .1) was
“arbitrarily” taken, let’s use the same procedure for an interval of
.025 (or a span of only 0.05). In this case, one would expect that we
need a bigger sample size, as the margin for “error” is smaller where we
would be confident enough that we have captured the true score.
combined_result_measures_within_interval <- combined_result_measures %>%
left_join(true_score, by = c("risk_dist_transl")) %>%
mutate(within_BC = if_else(BC_result >= mean_true_score_BC- lower_and_upper_interval_limit2 &
BC_result <= mean_true_score_BC + lower_and_upper_interval_limit2, 1, 0),
within_pol = if_else(polarization_result >= mean_true_score_pol- lower_and_upper_interval_limit2 &
polarization_result <= mean_true_score_pol + lower_and_upper_interval_limit2, 1, 0))
summarized_within_interval <-
combined_result_measures_within_interval %>%
group_by(risk_dist_transl, sample_size) %>%
summarize(count_within_BC = sum(within_BC),
count_within_pol = sum(within_pol),
perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
summarized_within_interval %>% ggplot(aes(factor(sample_size), percentage, col = risk_dist_transl, group = risk_dist_transl))+
geom_point()+
geom_line()+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.025 interval of true score",
x = "sample size",
title = "Interval of +- 0.025")+
scale_color_manual(values = colors)+
theme(legend.position = "none")

Difference between two
polarizations
The approach above would only tell us how likely it is that the
sampled distribution would lead to almost the same polarization value if
one had sampled 5’000 people (or in a sense, a “true score”, as it
converges to said population distribution). In the following section, we
will go one step further and analyse how likely it is to find the same
difference between two population distributions and two
sampled distributions.
true_score_long <- true_score %>%
pivot_longer(cols = starts_with("mean_true_score"),
names_prefix = "mean_true_score_",
names_to = "measure",
values_to = "value")
true_score_expanded <- expand.grid(x = true_score$risk_dist_transl, y =true_score$risk_dist_transl) %>%
filter(x != y) %>%
distinct() %>%
left_join(true_score, by = c("x" = "risk_dist_transl")) %>%
left_join(true_score, by = c("y" = "risk_dist_transl"), suffix = c("_x", "_y")) %>%
mutate(true_diff_BC = mean_true_score_BC_x - mean_true_score_BC_y,
true_diff_pol = mean_true_score_pol_x - mean_true_score_pol_y)
#because we have duplicate combinations (e.g. abs(x - y) == abs(y - x)), I'll sort them and take half of it (e.g. only the neccessary combinations)
n_unique <- nrow(true_score) * (nrow(true_score)-1) / 2
true_score_unique <- true_score_expanded %>%
arrange(desc(true_diff_BC)) %>%
slice_head(n = n_unique) %>%
select(x, y, true_diff_BC, true_diff_pol)
true_score_unique %>%
kable(digits = 2)
| Strong Pol. |
None 2 |
0.54 |
0.70 |
| Strong Pol. |
None |
0.50 |
0.68 |
| Rare |
None 2 |
0.42 |
0.06 |
| Rare |
None |
0.38 |
0.04 |
| Strong Pol. |
Rare |
0.12 |
0.64 |
| None |
None 2 |
0.04 |
0.02 |
These are the true score differences between two distributions if one
were to average the difference from sampling 5000 ratings for each
population.
In the following, we will calculate the difference between the
calculated polarization measures from another distribution. Again, we
will take two intervals: .1 and .05 (that is, the sampled difference is
allowed to deviate by .05 or .025 from the true score on either
side).
Interval of .1
combined_result_measures_selected <- combined_result_measures %>%
select(contains("result"), sample_size, risk_dist_transl) %>%
group_by(risk_dist_transl, sample_size) %>%
mutate(sample_ID = row_number())
diff_distributions <- true_score_unique %>%
left_join(combined_result_measures_selected, by = c("x" = "risk_dist_transl")) %>%
left_join(combined_result_measures_selected, by = c("y" = "risk_dist_transl", "sample_size", "sample_ID"), suffix = c("_x", "_y")) %>%
mutate(diff_BC = BC_result_x - BC_result_y,
diff_pol = polarization_result_x - polarization_result_y)
within_interval_diff_distributions <- diff_distributions %>%
mutate(within_BC = if_else(true_diff_BC >= diff_BC - lower_and_upper_interval_limit &
true_diff_BC <= diff_BC + lower_and_upper_interval_limit, 1, 0),
within_pol = if_else(true_diff_pol >= diff_pol - lower_and_upper_interval_limit &
true_diff_pol <= diff_pol + lower_and_upper_interval_limit, 1, 0),
comparison = paste(x, y, sep = " - ")
) %>%
group_by(comparison, sample_size) %>%
summarize(perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
within_interval_diff_distributions %>% ggplot(aes(factor(sample_size), percentage, col = factor(comparison), group = factor(comparison)))+
geom_point(size = 2)+
geom_line(linewidth = 1.2)+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.05 interval of true score",
x = "sample size",
title = "Interval of +- 0.05",
color = "Comparison between")+
scale_color_manual(values = colors2)+
theme(legend.spacing = unit(0.1, "cm"),
legend.margin = margin(0,0,0,0),
legend.box.margin = margin(0,0,0,0),
legend.position = c(.5,.05),
legend.direction = "horizontal")

Interval of .05
within_interval_diff_distributions <- diff_distributions %>%
mutate(within_BC = if_else(true_diff_BC >= diff_BC - lower_and_upper_interval_limit2 &
true_diff_BC <= diff_BC + lower_and_upper_interval_limit2, 1, 0),
within_pol = if_else(true_diff_pol >= diff_pol - lower_and_upper_interval_limit2 &
true_diff_pol <= diff_pol + lower_and_upper_interval_limit2, 1, 0),
comparison = paste(x, y, sep = " - ")
) %>%
group_by(comparison, sample_size) %>%
summarize(perc_within_BC = sum(within_BC) / replications_per_setting * 100,
perc_within_pol = sum(within_pol) / replications_per_setting * 100) %>%
pivot_longer(cols = starts_with("perc"),
names_to = "measure",
values_to = "percentage",
names_prefix = "perc_within_")
within_interval_diff_distributions %>% ggplot(aes(factor(sample_size), percentage, col = factor(comparison), group = factor(comparison)))+
geom_point(size = 2)+
geom_line(linewidth = 1.2)+
facet_wrap(~measure)+
theme_minimal()+
scale_y_continuous(breaks = seq(0, 100, by = 10))+
labs(y = "percentage of simulations within 0.025 interval of true score",
x = "sample size",
title = "Interval of +- 0.025",
color = "Comparison between")+
scale_color_manual(values = colors2)+
theme(legend.spacing = unit(0.1, "cm"),
legend.margin = margin(0,0,0,0),
legend.box.margin = margin(0,0,0,0),
legend.position = c(.5,.05),
legend.direction = "horizontal")

LS0tDQp0aXRsZTogIlJpc2sgUG9sYXJpemF0aW9uIFBvd2VyIEFuYWx5c2lzIHVzaW5nIERldmlhbmNlIg0KYXV0aG9yOiAiQW5keSBDYW8iDQpkYXRlOiAiMjAyNC0wNC0yMyINCm91dHB1dDogDQogICBodG1sX2RvY3VtZW50Og0KICAgICAgY3NzOiBzdHlsZXMuY3NzDQogICAgICB0b2M6IHRydWUNCiAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UNCiAgICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgICAgY29sbGFwc2VkOiB0cnVlDQogICAgICBzbW9vdGhfY29udHJvbGw6IGZhbHNlDQogICAgICBmaWcud2lkdGg6IDI2DQogICAgICBmaWcuaGVpZ2h0OiAyNg0KICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KIyBJbnRyb2R1Y3Rpb24gKyBNZXRob2RzDQoNCmBgYHtyIFNldHVwLCBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKSAjZGF0YSB3cmFuZ2xpbmcgYW5kIG90aGVyIHRvb2xzIGZvciBSDQpsaWJyYXJ5KGtuaXRyKSAjIHJlcG9ydCBnZW5lcmF0aW9uIGluIFINCmxpYnJhcnkocHN5Y2gpICNjYWxjdWxhdGUgc2tldyBhbmQga3VydG9zaXMgZm9yIEJDDQpsaWJyYXJ5KGFncm10KSAjZm9yIGFncmVlbWVudCBhbmQgcG9sYXJpemF0aW9uIGNhbGN1bGF0aW9uDQpsaWJyYXJ5KHZpc2RhdCkgI3Zpc3VhbGl6ZSBkYXRhZnJhbWVzIGluIHBsb3RzDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgI2Vhc3kgdG8gdXNlIGNvbG9yIHBhbGV0dGVzDQpsaWJyYXJ5KHJtYXJrZG93bikgI2ZvciB0aGUgcGFnZWRfdGFibGUgZnVuY3Rpb24NCmxpYnJhcnkoZG9QYXJhbGxlbCkgI3BhcmFsbGVsIGNvbXB1dGF0aW9uIHVzaW5nIG11bHRpcGxlIGNvcmVzDQpsaWJyYXJ5KGZvcmVhY2gpICMgZm9yIGVhY2ggZnVuY3Rpb24sIHNvIHRoZSBzaW11bGF0aW9uIGRvZXMgbm90IHRha2UgYWdlcw0KDQoNCmxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCA8LSAuMDUNCmxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdDIgPC0gLjAyNQ0KDQpjb2xvcnMgPC0gYnJld2VyLnBhbCg0LCAiRGFyazIiKQ0KY29sb3JzMiA8LSBicmV3ZXIucGFsKDYsICJTZXQyIikNCg0Kbl9zY2FsZV9vbl9saWtlcnQgPC0gYygxOjExKQ0KbWluX2xpa2VydCA8LSBtaW4obl9zY2FsZV9vbl9saWtlcnQpDQptYXhfbGlrZXJ0IDwtIG1heChuX3NjYWxlX29uX2xpa2VydCkNCg0Kc2FtcGxlX3NlcXVlbmNlIDwtIGMoMjAsMzAsNDAsNTAsIDYwLCA3MCwgODAsIDkwLCAxMDAsIDE1MCwgMjAwLHNlcSgyNTAsIDMwMDAsIGJ5ID0gMjUwKSwgNTAwMCkNCnNhbXBsZWQgPC0gZGF0YS5mcmFtZShOX3BhcnQgPSBzYW1wbGVfc2VxdWVuY2UpDQoNCnJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyA8LSA0MDANCg0Kbl9wb3B1bGF0aW9uIDwtIDEwXjQNCnByb3BfbWlub3JpdHkgPC0gLjA1DQoNCiNzZXQgc2VlZCBzbyBldmVyeSByYW5kb20gdGhpbmcgaGVyZSBpcyByZXByb2R1Y2libGUNCnNldC5zZWVkKDQyKQ0KDQojc2V0IGVjaG8gPSBGQUxTRSAoZS5nLiBkb250IHNob3cgY29kZSBpbiBvdXRwdXQpIGZvciBhbGwgY2h1bmtzLCBleGNlcHQgd2hlbiBleHBsaWNpdGx5IHRlbGxpbmcgb3RoZXJ3aXNlDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogICBlY2hvID0gVFJVRSwNCiAgIHdhcm5pbmcgPSBGQUxTRSwNCiAgIG1lc3NhZ2UgPSBGQUxTRQ0KICAgKQ0KYGBgDQoNClRoaXMgaXMgdGhlIDNyZCB0aW1lIEknbSB0cnlpbmcgdG8gZXN0aW1hdGUgdGhlIG5lZWRlZCBzYW1wbGUgc2l6ZSBmb3Igb3VyIHN0dWR5LiBBdCB0aGlzIHBvaW50LCB5b3Ugc2hvdWxkIChob3BlZnVsbHkpIGtub3cgdGhlIGRyaWxsLi4uICAgDQpUaGlzIHRpbWUgd2Ugd2lsbCB0cnkgdG8gZXN0aW1hdGUgb3VyIG5lZWRlZCBOIHdpdGggYW5vdGhlciBzaW11bGF0aW9uIGFwcHJvYWNoIHVzaW5nIGRldmlhbmNlIHRvIGEgInRydWUgc2NvcmUiIHdoaWNoIHdlIHdpbGwgZ2V0IHdpdGggc2FtcGxpbmcgYSBsb3QuDQoNCldlIHVzZSB0aGlzIHNhbXBsZSBzZXF1ZW5jZTogIA0KYGBge3J9DQpzYW1wbGVfc2VxdWVuY2UNCmBgYA0KDQpBbmQgZm9yIGVhY2ggb2YgdGhlc2Ugc2FtcGxlIHNpemVzLCB3ZSBzaW11bGF0ZSBgYHIgcmVwbGljYXRpb25zX3Blcl9zZXR0aW5nYGAgdGltZXMgKHdoaWxlIGhpZ2hlciBudW1iZXJzIHdvdWxkIGxlYWQgdG8gYSBtb3JlIHJvYnVzdC8gcmVsaWFibGUgZmluZGluZywgSSBzdGlsbCBuZWVkIHRvIG1ha2Ugc3VyZSB0aGF0IGl0IGNhbiBzdGlsbCBiZSBjb21wdXRhYmxlKS4uLg0KDQpIZXJlIGFyZSBvdXIgYXNzdW1lZCBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbiwgd2hpY2ggd2Ugd2lsbCBzYW1wbGUgZnJvbSBhZ2FpbjogIA0KDQpgYGB7ciBHZW5lcmF0aW9uIG9mIFBvcHVsYXRpb24gRGlzdHJpYnV0aW9ufQ0KDQpub3JtYWwgPC0gcm91bmQocm5vcm0obl9wb3B1bGF0aW9uLCBtZWFuID0gOSxzZCA9IDEuMikpDQpub3JtYWwgPC0gcG1pbihwbWF4KG5vcm1hbCwgbWluX2xpa2VydCksIG1heF9saWtlcnQpDQoNCnN0cm9uZ19wb2wgPC0gcm91bmQocmJldGEobl9wb3B1bGF0aW9uLCBzaGFwZTEgPSAuMSwgc2hhcGUyID0gLjEpKjEwKSArMQ0Kc3Ryb25nX3BvbCA8LSBwbWluKHBtYXgoc3Ryb25nX3BvbCwgbWluX2xpa2VydCksIG1heF9saWtlcnQpDQoNCnJhcmUgPC0gYyhyb3VuZChybm9ybShuX3BvcHVsYXRpb24gKigxLSBwcm9wX21pbm9yaXR5KSwgbWVhbiA9IDIsc2QgPSAxLjMpKSwgDQogICAgICAgICAgIHJvdW5kKHJub3JtKG5fcG9wdWxhdGlvbiAqcHJvcF9taW5vcml0eSwgbWVhbiA9IDExLHNkID0gLjUpKSkNCnJhcmUgPC0gcG1pbihwbWF4KHJhcmUsIG1pbl9saWtlcnQpLCBtYXhfbGlrZXJ0KQ0KDQpub3JtYWxfMiA8LSByb3VuZChybm9ybShuX3BvcHVsYXRpb24sIG1lYW4gPSA4LHNkID0gMSkpDQpub3JtYWxfMiA8LSBwbWluKHBtYXgobm9ybWFsXzIsIG1pbl9saWtlcnQpLCBtYXhfbGlrZXJ0KQ0KDQoNClBvcF9kZiA8LSBkYXRhLmZyYW1lKGBOb3JtYWwgRGlzdHJpYnV0aW9uYD0gbm9ybWFsLA0KICAgICAgICAgICAgICAgICAgICAgYE5vcm1hbCBEaXN0cmlidXRpb24gMmAgPSBub3JtYWxfMiwNCiAgICAgICAgICAgICAgICAgICAgIGBSYXJlIFBvbGFyaXphdGlvbmAgPSByYXJlLA0KICAgICAgICAgICAgICAgICAgICAgYFN0cm9uZyBQb2xhcml6YXRpb25gID0gc3Ryb25nX3BvbCkNCg0KbmFtZXMoUG9wX2RmKSA8LSBjKCJOb25lIiwgIk5vbmUgMiIsICJSYXJlIiwgIlN0cm9uZyBQb2wuIikNCg0KDQoNClBvcF9kZl9wbG90IDwtIFBvcF9kZiAlPiUgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgdmFsdWVzX3RvID0gIlJhdGluZyIsIG5hbWVzX3RvID0gIkRpc3RyIikgJT4lIA0KICAgbXV0YXRlKERpc3RyID0gZmFjdG9yKERpc3RyLCBsZXZlbHMgPSBjKCJOb25lIiwgIk5vbmUgMiIsIlJhcmUiLCAiU3Ryb25nIFBvbC4iKSkpDQoNClBvcF9kZl9wbG90ICU+JSBnZ3Bsb3QoYWVzKFJhdGluZywgZmlsbCA9IERpc3RyKSkrDQogICBnZW9tX2Jhcih3aWR0aD0gLjc1KSsNCiAgIGZhY2V0X2dyaWQocm93cyA9IHZhcnMoRGlzdHIpKSsNCiAgIHNjYWxlX3hfY29udGludW91cyhuLmJyZWFrcyA9IDExLCBleHBhbmQgPSBjKDAsMCkpKw0KICAgdGhlbWVfbWluaW1hbCgpKw0KICAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMSwgMiwgMCwgMCksDQogICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSsNCiAgIGdlb21fdGV4dChhZXMoeD02LHkgPSAzNTAwLCBsYWJlbD0gRGlzdHIsIGNvbG9yID0gRGlzdHIpLCBzaXplID0gNi44KSsNCiAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzKSsNCiAgIHlsYWIoIkNvdW50IikNCmBgYA0KDQpgYGB7ciBTYW1wbGluZywgY2FjaGU9VFJVRX0NCiMgbWF4X2NvbHMgPC0gbWF4KHNhbXBsZWQpDQojIA0KIyAjIFJlZ2lzdGVyIHBhcmFsbGVsIGJhY2tlbmQgd2l0aCB0aGUgZGVzaXJlZCBudW1iZXIgb2YgY29yZXMNCiMgbnVtX2NvcmVzIDwtIGRldGVjdENvcmVzKCktMQ0KIyANCiMgY2wgPC0gbWFrZUNsdXN0ZXIobnVtX2NvcmVzKQ0KIyByZWdpc3RlckRvUGFyYWxsZWwoY2wpDQojIA0KIyAjcm90YXRlIFBvcF9kZiwgc28gb3VyIGZ1bmN0aW9uIHdvcmtzIChlYWNoIHJvdyBuZWVkcyB0byBiZSBhIGRpZmZlcmVudCBkaXN0cmlidXRpb24sIGluc3RlYWQgb2YgZWFjaCBjb2wpDQojIHJvdF9Qb3BfZGYgPC0gYXMuZGF0YS5mcmFtZSh0KFBvcF9kZikpDQojIA0KIyByaXNrX2Rpc3RfdHIgPC0gZGF0YS5mcmFtZShyaXNrX2Rpc3RyaWJ1dGlvbiA9IDE6NCwNCiMgICAgICAgICAgICByaXNrX2Rpc3RfdHJhbnNsID0gcm93bmFtZXMocm90X1BvcF9kZikpDQojIA0KIyAjIERlZmluZSBmdW5jdGlvbiB0byBwcm9jZXNzIGVhY2ggY29tYmluYXRpb24gb2Ygcmlza19kaXN0cmlidXRpb24sIHNhbXBsZXNpemUgYW5kIHJlcGxpY2F0aW9ucyBwZXIgc2V0dGluZw0KIyBzYW1wbGVfYW5kX3JlcGxpY2F0ZV9mb3JfYWxsX3Jpc2tzIDwtIGZ1bmN0aW9uKGkpIHsNCiMgICBzYW1wbGVkX21hdHJpeF9saXN0IDwtIGxpc3QoKQ0KIyANCiMgICBmb3IgKGogaW4gMTpucm93KHJvdF9Qb3BfZGYpKSB7DQojICAgICBtYXQgPC0gcmVwbGljYXRlKHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZywNCiMgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlKDE6bl9wb3B1bGF0aW9uLCBzaXplID0gc2FtcGxlZFtpLCAxXSwgcmVwbGFjZSA9IFRSVUUpKSAjY3JlYXRlIG1hdHJpeCBvZiBvdXIgc2FtcGxlcyB3aXRoIHJlcGxhY2luZywgdGltZXMgbiAtIHJlcGxpY2F0aW9ucw0KIyANCiMgICAgIHNhbXBsZWRfdGFibGUgPC0gcm90X1BvcF9kZltqLCBtYXRdICN1c2luZyB0aGUgbWF0cml4LCBjb2xsZWN0IHRoZSB2YWx1ZXMgZnJvbSBvdXIgcmlzayBkaXN0cmlidXRpb24gbWF0cml4IChhcyBhIHZlY3RvciB0aG91Z2guLi4pDQojICAgICBzYW1wbGVkX21hdHJpeCA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChzYW1wbGVkX3RhYmxlLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBzYW1wbGVkW2ksIDFdLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnlyb3cgPSBUUlVFLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBOVUxMKSkgI2NyZWF0ZSBkZiBvdXQgb2YgdGhlc2UgdmVjdG9ycyBpbnN0ZWFkIG9mIGZsYXQgdmVjdG9ycw0KIyANCiMgDQojICAgICBpZiAobmNvbChzYW1wbGVkX21hdHJpeCkgPCBtYXhfY29scykgew0KIyAgICAgICBwYWRkaW5nX21hdHJpeCA8LSBtYXRyaXgoTkEsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gbnJvdyhzYW1wbGVkX21hdHJpeCksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gbWF4X2NvbHMgLSBuY29sKHNhbXBsZWRfbWF0cml4KSkNCiMgICAgICAgc2FtcGxlZF9tYXRyaXggPC0gY2JpbmQoc2FtcGxlZF9tYXRyaXgsIHBhZGRpbmdfbWF0cml4KQ0KIyAgICAgfSAjaWYgbWF0cml4IGlzIG5vdCB3aWRlIGVub3VnaCBmb3Igb3VyIGVuZCByZXN1bHQgbWF0cml4LCBwYWRkIGl0IHdpdGggTkEgY29sdW1ucywgc28gYmluZGluZyByb3dzIGlzIGRvYWJsZSAobmVlZHMgc2FtZSBhbW91bnQgb2YgbmNvbHMpDQojIA0KIyAgICAgc2FtcGxlZF9tYXRyaXggPC0gY2JpbmQobWF0cml4KHNhbXBsZWRbaSwxXSwgbnJvdyA9IHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRyaXgoaiwgbnJvdyA9IHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyksDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVkX21hdHJpeCkgI2JpbmQgY29sdW1ucyB3aXRoIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gc3VjaCBhcyBzYW1wbGUgc2l6ZSBhbmQgd2hpY2ggcmlza19kaXN0cmlidXRpb24gd2FzIHNhbXBsZWQNCiMgDQojICAgICBjb2xuYW1lcyhzYW1wbGVkX21hdHJpeCkgPC0gYygic2FtcGxlX3NpemUiLCAicmlza19kaXN0cmlidXRpb24iLCBwYXN0ZTAoInJhdGluZ18iLCAxOm1heF9jb2xzKSkgI3Jld3JpdGUgY29sbmFtZXMgc28gaXQgaXMgaWRlbnRpY2FsIHRvIHRoZSBiaWdoIG1hdHJpeA0KIyAgICAgc2FtcGxlZF9tYXRyaXhfbGlzdFtbal1dIDwtIHNhbXBsZWRfbWF0cml4ICNzdG9yZSBpbiBsaXN0DQojICAgfQ0KIyAgIHJldHVybihkby5jYWxsKHJiaW5kLCBzYW1wbGVkX21hdHJpeF9saXN0KSkgI2FmdGVyIGFsbCByaXNrIGRpc3RyaWJ1dGlvbnMgYXJlIHNhbXBsZWQgZnJvbSwgYmluZCB0aGVtIGFsbCBhbmQgcmV0dXJuIHRoZSBvdXRwdXQNCiMgDQojIH0NCiMgDQojICMgUGVyZm9ybSBwYXJhbGxlbCBwcm9jZXNzaW5nIHVzaW5nIGZvcmVhY2gsIGl0ZXJhdGluZyB0aHJvdWdoIHRoZSBkaWZmZXJlbnQgc2FtcGxlc2l6ZXMNCiMgcmVzdWx0IDwtIGZvcmVhY2goaSA9IDE6bnJvdyhzYW1wbGVkKSwgLmNvbWJpbmUgPSByYmluZCkgJWRvcGFyJSB7DQojICAgc2FtcGxlX2FuZF9yZXBsaWNhdGVfZm9yX2FsbF9yaXNrcyhpKQ0KIyB9DQojIA0KIyAjIFN0b3AgdGhlIHBhcmFsbGVsIGJhY2tlbmQNCiMgc3RvcENsdXN0ZXIoY2wpDQojIA0KIyByZXN1bHQgPC0gbXV0YXRlX2FsbChyZXN1bHQsIGFzLm51bWVyaWMpDQojIA0KIyANCiMgdmlzX21pc3MocmVzdWx0WyxzZXEoZnJvbSA9MywgdG8gPSBuY29sKHJlc3VsdCksIGxlbmd0aC5vdXQgPTIwKV0sIHNob3dfcGVyY19jb2wgPSBGKQ0KYGBgICANCg0KDQpgYGB7ciBDYWxjdWxhdGluZyBQb2xhcmlzYXRpb24gTWVhc3VyZXMsIGNhY2hlPVRSVUV9DQojdGFrZW4gZnJvbSBteSBwcmV2aW91cyB3b3JrDQoNCiMgY2FsY19iaW1vZGFsaXR5X2NvZWZmaWNpZW50IDwtIGZ1bmN0aW9uKHZlYyl7DQojICAgIHNrZXcgPC0gc2tldyh2ZWMsIG5hLnJtID0gVFJVRSwgdHlwZSA9IDMpDQojICAgIGt1cnRvc2lzIDwtIGt1cnRvc2kodmVjLCBuYS5ybSA9IFRSVUUsIHR5cGUgPSAzKQ0KIyAgICBuIDwtIHN1bSghaXMubmEodmVjKSkNCiMgICAgcmV0dXJuKChza2V3XjIrMSkgLyAoa3VydG9zaXMgKyAoKDMqKChuLTEpKV4yKS8oKG4tMikqKG4tMykpKSApKQ0KIyB9DQojIA0KIyAjIENyZWF0ZWQgdGhpcyBvbmUgImZyb20gc2NyYXRjaCIsIGFzIHdlIHdvcmsgd2l0aCBhIHNtYWxsZXIgc2NhbGUgb2YgMTEgaW5zdGVhZCBvZiAxMDEsIHRoZSBjYWxjdWxhdGlvbiBvZiBwb2xhcml6YXRpb24gZG9lcyBub3QgdGFrZSB0b28gbG9uZyBhbnltb3JlDQojIA0KIyBjYWxjX3BvbGFyaXphdGlvbiA8LSBmdW5jdGlvbih2ZWMpew0KIyAgICB2ZWMyIDwtIGFzLnZlY3Rvcih2ZWMpDQojICAgIGZyZXFfdmVjIDwtIGFncm10Ojpjb2xsYXBzZSh2ZWMyLCBwb3MgPSBjKDE6MTEpKQ0KIyAgICByZXR1cm4oYWdybXQ6OnBvbGFyaXphdGlvbihmcmVxX3ZlYykpDQojIH0NCiMgDQojICMgdGhpcyBjb2RlIGlzIGNvbW1lbnRlZCwgYXMgdGhlIGNvbXB1dGF0aW9uIHRha2VzIHRvbyBsb25nLCBhbmQgSSBoYXRlIHdhaXRpbmcgd2hpbGUga25pdGluZy4gSSBoYXZlIHNhdmVkIGEgc2VwcGFyYXRlIHJkcyBmaWxlLCB3aGljaCB3aWxsIGJlIHJlYWQgaW4gaW5zdGVhZC4gUmVhZGVycyB3aG8gd2FudCB0byB1bmNvbW1lbnQgc2VjdGlvbiwgc2VsZWN0IHRoZSBsaW5lcyB0byB1bmNvbW1lbnQgYW5kIHByZXNzIEN0cmwgKyBTaGlmdCArIEMgKG9uIFdpbmRvd3MvTGludXgpIG9yIENtZCArIFNoaWZ0ICsgQyAob24gbWFjT1MpDQojICMNCiMgIyAjYXBwbHkgdGhlIGZ1bmN0aW9ucyB0byBvdXIgcmVzdWx0IG1hdHJpeCwgdGhlcmVmb3JlIGNhbGN1bGF0ZSBmb3IgZWFjaCBkcmF3biBzYW1wbGUgdGhlIHBvbGFyaXphdGlvbiBtZXRyaWNzDQojIEJDX3Jlc3VsdCA8LSBhcHBseShyZXN1bHRbLC1jKDE6MildLCAxLCBjYWxjX2JpbW9kYWxpdHlfY29lZmZpY2llbnQpDQojIHN1bShpcy5uYShCQ19yZXN1bHQpKQ0KIyANCiMgDQojICMgUmVnaXN0ZXIgcGFyYWxsZWwgYmFja2VuZCB3aXRoIHRoZSBkZXNpcmVkIG51bWJlciBvZiBjb3Jlcw0KIyBudW1fY29yZXMgPC0gZGV0ZWN0Q29yZXMoKS0xDQojIA0KIyBjbCA8LSBtYWtlQ2x1c3RlcihudW1fY29yZXMpDQojIHJlZ2lzdGVyRG9QYXJhbGxlbChjbCkNCiMgDQojICMgUGVyZm9ybSBwYXJhbGxlbCBwcm9jZXNzaW5nIHVzaW5nIGZvcmVhY2gsIGl0ZXJhdGluZyB0aHJvdWdoIHRoZSBkaWZmZXJlbnQgZHJhd24gc2FtcGxlcw0KIyBwb2xhcml6YXRpb25fcmVzdWx0IDwtIGZvcmVhY2goaSA9IDE6bnJvdyhyZXN1bHQpLCAuY29tYmluZSA9IHJiaW5kKSAlZG9wYXIlIHsNCiMgICBjYWxjX3BvbGFyaXphdGlvbihyZXN1bHRbaSwgLWMoMToyKV0pDQojIH0NCiMgc3VtKGlzLm5hKHBvbGFyaXphdGlvbl9yZXN1bHQpKQ0KIyANCiMgIyBTdG9wIHRoZSBwYXJhbGxlbCBiYWNrZW5kDQojIHN0b3BDbHVzdGVyKGNsKQ0KIyANCiMgDQojIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyA8LSBjYmluZChwb2xhcml6YXRpb25fcmVzdWx0LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQkNfcmVzdWx0LA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0WywxOjJdDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQojIHN1bShpcy5uYShjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMpKQ0KIyANCiMgDQojIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lDQojICAgIGxlZnRfam9pbihyaXNrX2Rpc3RfdHIsIGJ5ID0gInJpc2tfZGlzdHJpYnV0aW9uIikgJT4lDQojICAgIG11dGF0ZShmc2FtcGxlX3NpemUgPSBmYWN0b3Ioc2FtcGxlX3NpemUsIG9yZGVyZWQgPSBUKSkNCiMgDQojIHNhdmVSRFMoY29tYmluZWRfcmVzdWx0X21lYXN1cmVzLCAic2F2ZWRfUkRTXFxjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXNfcG93ZXJfYW5hbHlzaXNfZGV2aWFuY2UucmRzIikNCg0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzIDwtIHJlYWRSRFMoInNhdmVkX1JEU1xcY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3Bvd2VyX2FuYWx5c2lzX2RldmlhbmNlLnJkcyIpDQoNCg0KDQp2aXNfbWlzcyhjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMsIHNvcnRfbWlzcyA9IEYpDQpgYGANCg0KIyBSZXN1bHRzDQoNCkluIHRoZSBmb2xsb3dpbmcgZGVuc2l0eSBwbG90cywgd2Ugc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCBjYWxjdWxhdGVkICoqcG9sYXJpemF0aW9uKiogbWVhc3VyZSAoZnJvbSB0aGUgYm9vayAiVHJpZ2dlcnB1bmt0ZSIgZnJvbSBNYXUgZXQgYWwuKSwgd2hpY2ggcmFuZ2VzIGZyb20gMCAobm90IHBvbGFyaXplZCwgcGVvcGxlIGFyZSBpbiBhZ3JlZW1lbnQpIHRvIDEgKGhpZ2hlc3QgcG9sYXJpemF0aW9uKS4gV2UgY2FuIHNlZSB0aGF0IHRoZSBtb3JlIHdlIHNhbXBsZSwgdGhlIG1vcmUgdGhlIHJlc3VsdHMgY29udmVyZ2UgdG8gYSAidHJ1ZSIgc2NvcmUgZm9yIHNhaWQgYXNzdW1lZCBwb3B1bGF0aW9uIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3J9DQpjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgZ2dwbG90KGFlcyhwb2xhcml6YXRpb25fcmVzdWx0KSkrDQogICBnZW9tX2RlbnNpdHkoYWVzKGZpbGwgPSBmc2FtcGxlX3NpemUpLCBhbHBoYSA9IC40KSsNCiAgIGZhY2V0X2dyaWQocm93cyA9dmFycyhyaXNrX2Rpc3RfdHJhbnNsKSkNCmBgYA0KDQpBbmQgZm9yIHRoZSBiaW1vZGFsaXR5IENvZWZmaWNpZW50IGFzIHdlbGw6DQoNCmBgYHtyfQ0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JSANCiAgIGdncGxvdChhZXMoQkNfcmVzdWx0KSkrDQogICBnZW9tX2RlbnNpdHkoYWVzKGZpbGwgPSBmc2FtcGxlX3NpemUpLCBhbHBoYSA9IC40KSsNCiAgIGZhY2V0X2dyaWQocm93cyA9dmFycyhyaXNrX2Rpc3RfdHJhbnNsKSkNCmBgYA0KDQpJbiB0aGUgZm9sbG93aW5nLCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgKiptb2RlKiogb2YgZWFjaCBkZW5zaXR5IHBsb3QgKHRoYXQgaXMsIHdoZXJlIHRoZSBwb2xhcml6YXRpb24gbWVhc3VyZXMgY29udmVyZ2VzIG9yIGhhcyBhIGhpZ2ggcHJvYmFiaWxpdHkgdGhhdCBpdCBlbmRzIHVwIG9uIFgsICoqaWYgd2Ugd2VyZSB0byBzYW1wbGUgaXQgZmV3ZXIgdGltZXMqKikuIFdlIGNhbiB0aGVuIHBsb3QgdGhlc2UgZmluZGluZ3MsIGFuZCBzaG91bGQgbG9vayBhdCB0aGUgcG9pbnQgd2hlcmUgdGhlICoqbW9kZXMqKiBjb252ZXJnZXMgdG93YXJkcyB0aGUgInRydWUgc2NvcmUiIChpbiBvdXIgY2FzZSwgdGhlIHNhbXBsZSBzaXplIG9mIDUwMDApLg0KDQpgYGB7ciwgZmlnLndpZHRoPTE0fQ0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzIDwtIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyAlPiUgDQogICBtdXRhdGUoZ3JvdXBlZF9kaXN0WHNhbXBsZSA9IHBhc3RlKHJpc2tfZGlzdF90cmFuc2wsIGZzYW1wbGVfc2l6ZSwgc2VwID0gIl8iKSkNCg0Kc3BsaXR0ZWQgPC0gc3BsaXQoY29tYmluZWRfcmVzdWx0X21lYXN1cmVzJHBvbGFyaXphdGlvbl9yZXN1bHQsIGYgPSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMkZ3JvdXBlZF9kaXN0WHNhbXBsZSkNCnNwbGl0dGVkMiA8LSBzcGxpdChjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMkQkNfcmVzdWx0LCBmID0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzJGdyb3VwZWRfZGlzdFhzYW1wbGUpDQoNCm1vZGVfZGVuc19yZXMgPC0gZGF0YS5mcmFtZSgpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChzcGxpdHRlZCkpIHsNCg0KICAgZGVuc19yZXMgPC0gZGVuc2l0eShzcGxpdHRlZFtbaV1dKQ0KICAgZGVuc19yZXMyIDwtIGRlbnNpdHkoc3BsaXR0ZWQyW1tpXV0pDQogICANCiAgIGRlbnNfZGYgPC0gZGF0YS5mcmFtZShncm91cCA9IG5hbWVzKHNwbGl0dGVkKVtpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlX2RlbnNfcG9sID0gZGVuc19yZXMkeFt3aGljaC5tYXgoZGVuc19yZXMkeSldLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVfZGVuc19CQyA9IGRlbnNfcmVzMiR4W3doaWNoLm1heChkZW5zX3JlczIkeSldKQ0KICAgDQogICBtb2RlX2RlbnNfcmVzIDwtIHJiaW5kKG1vZGVfZGVuc19yZXMsIGRlbnNfZGYpDQogICANCn0NCg0KY2xlYW5lZF9tb2RlX2RlbnNfcmVzIDwtIG1vZGVfZGVuc19yZXMgJT4lIA0KICAgc2VwYXJhdGUoZ3JvdXAsIHNlcCA9ICJfIiwgaW50byA9IGMoIkRpc3RyaWJ1dGlvbiIsICJmc2FtcGxlX3NpemUiKSkgJT4lIA0KICAgbXV0YXRlKFNhbXBsZV9TaXplID0gYXMuaW50ZWdlcihmc2FtcGxlX3NpemUpKSAlPiUgDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJtb2RlX2RlbnMiKSwNCiAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAibW9kZV9kZW5zXyIsDQogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZSIsDQogICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikNCiAgIA0KDQpjbGVhbmVkX21vZGVfZGVuc19yZXMgJT4lIA0KICAgZ2dwbG90KGFlcyhTYW1wbGVfU2l6ZSwgdmFsdWUsIGdyb3VwID0gRGlzdHJpYnV0aW9uLCBjb2wgPSBEaXN0cmlidXRpb24pKSsNCiAgIGdlb21fbGluZShzaXplPSAxLjEpKw0KICAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhtZWFzdXJlKSkrDQogICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2FtcGxlX3NlcXVlbmNlKSsNCiAgIHRoZW1lX21pbmltYWwoKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpDQoNCmBgYA0KDQojIyBQcm9iYWJpbGl0eSBvZiBTYW1wbGUgYmVpbmcgaW4gVHJ1ZSBTY29yZSBJbnRlcnZhbCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpXaGlsZSB0aGUgbW9kZSBjb3VsZCBhIGJlIGdvb2QgaW5kaWNhdG9yLCBvbmUgbWlnaHQgYWxzbyBhcmd1ZSB0aGF0IGl0IGlzIG1vcmUgcmVsZXZhbnQgdG8gaGF2ZSBhIGdvb2QgY2hhbmNlIG9mIGNhcHR1cmluZyB0aGUgdHJ1ZSBwb2xhcml6YXRpb24gdmFsdWUuIFRoYXQgaXMsIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBtZWFzdXJlIGlzIG9ubHkgb2ZmIGJ5IG9ubHkgLjEgKG1lYW5pbmcgaXQgaXMgYWx3YXlzIHdpdGhpbiBhIGludGVydmFsIG9mICsgMC4wNSBhbmQgLSAwLjA1KSBmcm9tIHRoZSB0cnVlIHNjb3JlLiBTbyBsZXQncyBjYWxjdWxhdGUgdGhlICUgd2hlcmUgdGhlIGBgciByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmdgYCBzaW11bGF0aW9ucyBwZXIgc2FtcGxlIHNpemUgd2FzIHdpdGhpbiB0aGlzIGludGVydmFsLiBPbmUgY291bGQgdGhlbiBpbnRlcnByZXQgdGhlIHJlc3VsdHMgYXM6ICoqdGhlIHByb2JhYmlsaXR5IG9mIGNvbWluZyB0byB0aGUgc2FtZSBwb2xhcml6YXRpb24gdmFsdWUgKHdpdGggYW4gaW50ZXJ2YWwgb2YgLjEpIGNvbXBhcmVkIHRvIGlmIHdlIGhhZCBzYW1wbGVkIDUnMDAwIHBlb3BsZSoqLg0KDQojIyMgSW50ZXJ2YWwgb2YgLjENCg0KYGBge3IsIGZpZy53aWR0aD0gMTR9DQojIGNhbGN1bGF0ZSB0aGUgInRydWUgc2NvcmUiIHZhbHVlIG9mIHNhaWQgcG9sYXJpemF0aW9uIG1lYXN1cmUgZm9yIGVhY2ggcG9wdWxhdGlvbiBkaXN0cmlidXRpb24NCg0KdHJ1ZV9zY29yZSA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgZmlsdGVyKHNhbXBsZV9zaXplID09IDUwMDApICU+JSANCiAgIGdyb3VwX2J5KHJpc2tfZGlzdF90cmFuc2wpICU+JSANCiAgIHN1bW1hcml6ZShtZWFuX3RydWVfc2NvcmVfQkMgPSBtZWFuKEJDX3Jlc3VsdCksDQogICAgICAgICAgICAgbWVhbl90cnVlX3Njb3JlX3BvbCA9IG1lYW4ocG9sYXJpemF0aW9uX3Jlc3VsdCkpDQoNCg0KDQoNCmNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc193aXRoaW5faW50ZXJ2YWwgPC0gY29tYmluZWRfcmVzdWx0X21lYXN1cmVzICU+JSANCiAgIGxlZnRfam9pbih0cnVlX3Njb3JlLCBieSA9IGMoInJpc2tfZGlzdF90cmFuc2wiKSkgJT4lIA0KICAgbXV0YXRlKHdpdGhpbl9CQyA9IGlmX2Vsc2UoQkNfcmVzdWx0ID49IG1lYW5fdHJ1ZV9zY29yZV9CQy0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0ICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCQ19yZXN1bHQgPD0gbWVhbl90cnVlX3Njb3JlX0JDICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0LCAxLCAwKSwNCiAgICAgICAgICB3aXRoaW5fcG9sID0gaWZfZWxzZShwb2xhcml6YXRpb25fcmVzdWx0ID49IG1lYW5fdHJ1ZV9zY29yZV9wb2wtIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9sYXJpemF0aW9uX3Jlc3VsdCA8PSBtZWFuX3RydWVfc2NvcmVfcG9sICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0LCAxLCAwKSkNCg0Kc3VtbWFyaXplZF93aXRoaW5faW50ZXJ2YWwgPC0NCiAgIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc193aXRoaW5faW50ZXJ2YWwgJT4lDQogICBncm91cF9ieShyaXNrX2Rpc3RfdHJhbnNsLCBzYW1wbGVfc2l6ZSkgJT4lDQogICBzdW1tYXJpemUoY291bnRfd2l0aGluX0JDID0gc3VtKHdpdGhpbl9CQyksDQogICAgICBjb3VudF93aXRoaW5fcG9sID0gc3VtKHdpdGhpbl9wb2wpLA0KICAgICAgcGVyY193aXRoaW5fQkMgPSBzdW0od2l0aGluX0JDKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCwNCiAgICAgIHBlcmNfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCkgJT4lDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJwZXJjIiksDQogICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgIHZhbHVlc190byA9ICJwZXJjZW50YWdlIiwNCiAgICAgIG5hbWVzX3ByZWZpeCA9ICJwZXJjX3dpdGhpbl8iKQ0KDQpzdW1tYXJpemVkX3dpdGhpbl9pbnRlcnZhbCAlPiUgZ2dwbG90KGFlcyhmYWN0b3Ioc2FtcGxlX3NpemUpLCBwZXJjZW50YWdlLCBjb2wgPSByaXNrX2Rpc3RfdHJhbnNsLCBncm91cCA9IHJpc2tfZGlzdF90cmFuc2wpKSsNCiAgIGdlb21fcG9pbnQoKSsNCiAgIGdlb21fbGluZSgpKw0KICAgZmFjZXRfd3JhcCh+bWVhc3VyZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpKw0KICAgbGFicyh5ID0gInBlcmNlbnRhZ2Ugb2Ygc2ltdWxhdGlvbnMgd2l0aGluIDAuMDUgaW50ZXJ2YWwgb2YgdHJ1ZSBzY29yZSIsDQogICAgICAgIHggPSAic2FtcGxlIHNpemUiLA0KICAgICAgICB0aXRsZSA9ICJJbnRlcnZhbCBvZiArLSAwLjA1IikrDQogICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sb3JzKSsNCiAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCiAgIA0KDQpgYGANCg0KIyMjIEludGVydmFsIG9mIC4wNSB7LmFjdGl2ZX0NCg0KQXMgdGhlIGludGVydmFsIG9mIC4wNSAod2hpY2ggcmVzdWx0cyBvZiBhIGNvdmVyYWdlIG9mIC4xKSB3YXMgImFyYml0cmFyaWx5IiB0YWtlbiwgbGV0J3MgdXNlIHRoZSBzYW1lIHByb2NlZHVyZSBmb3IgYW4gaW50ZXJ2YWwgb2YgLjAyNSAob3IgYSBzcGFuIG9mIG9ubHkgMC4wNSkuIEluIHRoaXMgY2FzZSwgb25lIHdvdWxkIGV4cGVjdCB0aGF0IHdlIG5lZWQgYSBiaWdnZXIgc2FtcGxlIHNpemUsIGFzIHRoZSBtYXJnaW4gZm9yICJlcnJvciIgaXMgc21hbGxlciB3aGVyZSB3ZSB3b3VsZCBiZSBjb25maWRlbnQgZW5vdWdoIHRoYXQgd2UgaGF2ZSBjYXB0dXJlZCB0aGUgdHJ1ZSBzY29yZS4NCg0KDQpgYGB7ciwgZmlnLndpZHRoPTE0fQ0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3dpdGhpbl9pbnRlcnZhbCA8LSBjb21iaW5lZF9yZXN1bHRfbWVhc3VyZXMgJT4lIA0KICAgbGVmdF9qb2luKHRydWVfc2NvcmUsIGJ5ID0gYygicmlza19kaXN0X3RyYW5zbCIpKSAlPiUgDQogICBtdXRhdGUod2l0aGluX0JDID0gaWZfZWxzZShCQ19yZXN1bHQgPj0gbWVhbl90cnVlX3Njb3JlX0JDLSBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCQ19yZXN1bHQgPD0gbWVhbl90cnVlX3Njb3JlX0JDICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiwgMSwgMCksDQogICAgICAgICAgd2l0aGluX3BvbCA9IGlmX2Vsc2UocG9sYXJpemF0aW9uX3Jlc3VsdCA+PSBtZWFuX3RydWVfc2NvcmVfcG9sLSBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb2xhcml6YXRpb25fcmVzdWx0IDw9IG1lYW5fdHJ1ZV9zY29yZV9wb2wgKyBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyLCAxLCAwKSkNCg0Kc3VtbWFyaXplZF93aXRoaW5faW50ZXJ2YWwgPC0NCiAgIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc193aXRoaW5faW50ZXJ2YWwgJT4lDQogICBncm91cF9ieShyaXNrX2Rpc3RfdHJhbnNsLCBzYW1wbGVfc2l6ZSkgJT4lDQogICBzdW1tYXJpemUoY291bnRfd2l0aGluX0JDID0gc3VtKHdpdGhpbl9CQyksDQogICAgICBjb3VudF93aXRoaW5fcG9sID0gc3VtKHdpdGhpbl9wb2wpLA0KICAgICAgcGVyY193aXRoaW5fQkMgPSBzdW0od2l0aGluX0JDKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCwNCiAgICAgIHBlcmNfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCkgJT4lDQogICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJwZXJjIiksDQogICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgIHZhbHVlc190byA9ICJwZXJjZW50YWdlIiwNCiAgICAgIG5hbWVzX3ByZWZpeCA9ICJwZXJjX3dpdGhpbl8iKQ0KDQpzdW1tYXJpemVkX3dpdGhpbl9pbnRlcnZhbCAlPiUgZ2dwbG90KGFlcyhmYWN0b3Ioc2FtcGxlX3NpemUpLCBwZXJjZW50YWdlLCBjb2wgPSByaXNrX2Rpc3RfdHJhbnNsLCBncm91cCA9IHJpc2tfZGlzdF90cmFuc2wpKSsNCiAgIGdlb21fcG9pbnQoKSsNCiAgIGdlb21fbGluZSgpKw0KICAgZmFjZXRfd3JhcCh+bWVhc3VyZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpKw0KICAgbGFicyh5ID0gInBlcmNlbnRhZ2Ugb2Ygc2ltdWxhdGlvbnMgd2l0aGluIDAuMDI1IGludGVydmFsIG9mIHRydWUgc2NvcmUiLA0KICAgICAgICB4ID0gInNhbXBsZSBzaXplIiwNCiAgICAgICAgdGl0bGUgPSAiSW50ZXJ2YWwgb2YgKy0gMC4wMjUiKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpKw0KICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KIyMgRGlmZmVyZW5jZSBiZXR3ZWVuIHR3byBwb2xhcml6YXRpb25zIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClRoZSBhcHByb2FjaCBhYm92ZSB3b3VsZCBvbmx5IHRlbGwgdXMgaG93IGxpa2VseSBpdCBpcyB0aGF0IHRoZSBzYW1wbGVkIGRpc3RyaWJ1dGlvbiB3b3VsZCBsZWFkIHRvIGFsbW9zdCB0aGUgc2FtZSBwb2xhcml6YXRpb24gdmFsdWUgaWYgb25lIGhhZCBzYW1wbGVkIDUnMDAwIHBlb3BsZSAob3IgaW4gYSBzZW5zZSwgYSAidHJ1ZSBzY29yZSIsIGFzIGl0IGNvbnZlcmdlcyB0byBzYWlkIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9uKS4gSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uLCB3ZSB3aWxsIGdvIG9uZSBzdGVwIGZ1cnRoZXIgYW5kIGFuYWx5c2UgaG93IGxpa2VseSBpdCBpcyB0byBmaW5kIHRoZSBzYW1lIGRpZmZlcmVuY2UgYmV0d2VlbiAqKnR3byoqIHBvcHVsYXRpb24gZGlzdHJpYnV0aW9ucyBhbmQgdHdvIHNhbXBsZWQgZGlzdHJpYnV0aW9ucy4gIA0KDQpgYGB7cn0NCnRydWVfc2NvcmVfbG9uZyA8LSB0cnVlX3Njb3JlICU+JSANCiAgIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoIm1lYW5fdHJ1ZV9zY29yZSIpLA0KICAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJtZWFuX3RydWVfc2NvcmVfIiwNCiAgICAgICAgICAgICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKQ0KDQoNCnRydWVfc2NvcmVfZXhwYW5kZWQgPC0gZXhwYW5kLmdyaWQoeCA9IHRydWVfc2NvcmUkcmlza19kaXN0X3RyYW5zbCwgeSA9dHJ1ZV9zY29yZSRyaXNrX2Rpc3RfdHJhbnNsKSAlPiUgDQogICBmaWx0ZXIoeCAhPSB5KSAlPiUgDQogICBkaXN0aW5jdCgpICU+JSANCiAgIGxlZnRfam9pbih0cnVlX3Njb3JlLCBieSA9IGMoIngiID0gInJpc2tfZGlzdF90cmFuc2wiKSkgJT4lIA0KICAgbGVmdF9qb2luKHRydWVfc2NvcmUsIGJ5ID0gYygieSIgPSAicmlza19kaXN0X3RyYW5zbCIpLCBzdWZmaXggPSBjKCJfeCIsICJfeSIpKSAlPiUgDQogICBtdXRhdGUodHJ1ZV9kaWZmX0JDID0gbWVhbl90cnVlX3Njb3JlX0JDX3ggLSBtZWFuX3RydWVfc2NvcmVfQkNfeSwNCiAgICAgICAgICB0cnVlX2RpZmZfcG9sID0gbWVhbl90cnVlX3Njb3JlX3BvbF94IC0gbWVhbl90cnVlX3Njb3JlX3BvbF95KQ0KDQoNCiNiZWNhdXNlIHdlIGhhdmUgZHVwbGljYXRlIGNvbWJpbmF0aW9ucyAoZS5nLiBhYnMoeCAtIHkpID09IGFicyh5IC0geCkpLCBJJ2xsIHNvcnQgdGhlbSBhbmQgdGFrZSBoYWxmIG9mIGl0IChlLmcuIG9ubHkgdGhlIG5lY2Nlc3NhcnkgY29tYmluYXRpb25zKQ0Kbl91bmlxdWUgPC0gbnJvdyh0cnVlX3Njb3JlKSAqIChucm93KHRydWVfc2NvcmUpLTEpIC8gMg0KDQp0cnVlX3Njb3JlX3VuaXF1ZSA8LSB0cnVlX3Njb3JlX2V4cGFuZGVkICU+JSANCiAgIGFycmFuZ2UoZGVzYyh0cnVlX2RpZmZfQkMpKSAlPiUgDQogICBzbGljZV9oZWFkKG4gPSBuX3VuaXF1ZSkgJT4lIA0KICAgc2VsZWN0KHgsIHksIHRydWVfZGlmZl9CQywgdHJ1ZV9kaWZmX3BvbCkNCg0KDQoNCnRydWVfc2NvcmVfdW5pcXVlICU+JSANCiAgIGthYmxlKGRpZ2l0cyA9IDIpDQpgYGANCg0KVGhlc2UgYXJlIHRoZSB0cnVlIHNjb3JlIGRpZmZlcmVuY2VzIGJldHdlZW4gdHdvIGRpc3RyaWJ1dGlvbnMgaWYgb25lIHdlcmUgdG8gYXZlcmFnZSB0aGUgZGlmZmVyZW5jZSBmcm9tIHNhbXBsaW5nIDUwMDAgcmF0aW5ncyBmb3IgZWFjaCBwb3B1bGF0aW9uLiAgDQoNCg0KSW4gdGhlIGZvbGxvd2luZywgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgY2FsY3VsYXRlZCBwb2xhcml6YXRpb24gbWVhc3VyZXMgZnJvbSBhbm90aGVyIGRpc3RyaWJ1dGlvbi4gQWdhaW4sIHdlIHdpbGwgdGFrZSB0d28gaW50ZXJ2YWxzOg0KLjEgYW5kIC4wNSAodGhhdCBpcywgdGhlIHNhbXBsZWQgZGlmZmVyZW5jZSBpcyBhbGxvd2VkIHRvIGRldmlhdGUgYnkgLjA1IG9yIC4wMjUgZnJvbSB0aGUgdHJ1ZSBzY29yZSBvbiBlaXRoZXIgc2lkZSkuDQoNCiMjIyBJbnRlcnZhbCBvZiAuMQ0KDQpgYGB7ciwgZmlnLndpZHRoPSAxNH0NCg0KY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3NlbGVjdGVkIDwtIGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlcyAlPiUgDQogICBzZWxlY3QoY29udGFpbnMoInJlc3VsdCIpLCBzYW1wbGVfc2l6ZSwgcmlza19kaXN0X3RyYW5zbCkgJT4lIA0KICAgZ3JvdXBfYnkocmlza19kaXN0X3RyYW5zbCwgc2FtcGxlX3NpemUpICU+JSANCiAgIG11dGF0ZShzYW1wbGVfSUQgPSByb3dfbnVtYmVyKCkpDQoNCmRpZmZfZGlzdHJpYnV0aW9ucyA8LSB0cnVlX3Njb3JlX3VuaXF1ZSAlPiUgDQogICBsZWZ0X2pvaW4oY29tYmluZWRfcmVzdWx0X21lYXN1cmVzX3NlbGVjdGVkLCBieSA9IGMoIngiID0gInJpc2tfZGlzdF90cmFuc2wiKSkgJT4lIA0KICAgbGVmdF9qb2luKGNvbWJpbmVkX3Jlc3VsdF9tZWFzdXJlc19zZWxlY3RlZCwgYnkgPSBjKCJ5IiA9ICJyaXNrX2Rpc3RfdHJhbnNsIiwgInNhbXBsZV9zaXplIiwgInNhbXBsZV9JRCIpLCBzdWZmaXggPSBjKCJfeCIsICJfeSIpKSAlPiUgDQogICBtdXRhdGUoZGlmZl9CQyA9IEJDX3Jlc3VsdF94IC0gQkNfcmVzdWx0X3ksDQogICAgICAgICAgZGlmZl9wb2wgPSBwb2xhcml6YXRpb25fcmVzdWx0X3ggLSBwb2xhcml6YXRpb25fcmVzdWx0X3kpDQoNCndpdGhpbl9pbnRlcnZhbF9kaWZmX2Rpc3RyaWJ1dGlvbnMgPC0gZGlmZl9kaXN0cmlidXRpb25zICU+JSANCiAgIG11dGF0ZSh3aXRoaW5fQkMgPSBpZl9lbHNlKHRydWVfZGlmZl9CQyA+PSBkaWZmX0JDIC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0ICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWVfZGlmZl9CQyA8PSBkaWZmX0JDICsgbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0LCAxLCAwKSwNCiAgICAgICAgICB3aXRoaW5fcG9sID0gaWZfZWxzZSh0cnVlX2RpZmZfcG9sID49IGRpZmZfcG9sIC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0ICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWVfZGlmZl9wb2wgPD0gZGlmZl9wb2wgKyBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQsIDEsIDApLA0KICAgICAgICAgIGNvbXBhcmlzb24gPSBwYXN0ZSh4LCB5LCBzZXAgPSAiIC0gIikNCiAgICkgJT4lIA0KICAgZ3JvdXBfYnkoY29tcGFyaXNvbiwgc2FtcGxlX3NpemUpICU+JSANCiAgIHN1bW1hcml6ZShwZXJjX3dpdGhpbl9CQyA9IHN1bSh3aXRoaW5fQkMpIC8gcmVwbGljYXRpb25zX3Blcl9zZXR0aW5nICogMTAwLA0KICAgICAgICAgICAgIHBlcmNfd2l0aGluX3BvbCA9IHN1bSh3aXRoaW5fcG9sKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCkgJT4lIA0KICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgicGVyYyIpLA0KICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1lYXN1cmUiLA0KICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJwZXJjZW50YWdlIiwNCiAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAicGVyY193aXRoaW5fIikNCiAgIA0KDQp3aXRoaW5faW50ZXJ2YWxfZGlmZl9kaXN0cmlidXRpb25zICU+JSBnZ3Bsb3QoYWVzKGZhY3RvcihzYW1wbGVfc2l6ZSksIHBlcmNlbnRhZ2UsIGNvbCA9IGZhY3Rvcihjb21wYXJpc29uKSwgZ3JvdXAgPSBmYWN0b3IoY29tcGFyaXNvbikpKSsNCiAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpKw0KICAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEuMikrDQogICBmYWNldF93cmFwKH5tZWFzdXJlKSsNCiAgIHRoZW1lX21pbmltYWwoKSsNCiAgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkrDQogICBsYWJzKHkgPSAicGVyY2VudGFnZSBvZiBzaW11bGF0aW9ucyB3aXRoaW4gMC4wNSBpbnRlcnZhbCBvZiB0cnVlIHNjb3JlIiwNCiAgICAgICAgeCA9ICJzYW1wbGUgc2l6ZSIsDQogICAgICAgIHRpdGxlID0gIkludGVydmFsIG9mICstIDAuMDUiLA0KICAgICAgICBjb2xvciA9ICJDb21wYXJpc29uIGJldHdlZW4iKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMyKSsNCiAgIHRoZW1lKGxlZ2VuZC5zcGFjaW5nID0gdW5pdCgwLjEsICJjbSIpLA0KICAgICAgICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLDAsMCwwKSwNCiAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsMCwwLDApLA0KICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYyguNSwuMDUpLA0KICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikNCiAgIA0KDQoNCmBgYA0KDQojIyMgSW50ZXJ2YWwgb2YgLjA1IHsuYWN0aXZlfQ0KYGBge3IsIGZpZy53aWR0aD0gMTR9DQoNCndpdGhpbl9pbnRlcnZhbF9kaWZmX2Rpc3RyaWJ1dGlvbnMgPC0gZGlmZl9kaXN0cmlidXRpb25zICU+JSANCiAgIG11dGF0ZSh3aXRoaW5fQkMgPSBpZl9lbHNlKHRydWVfZGlmZl9CQyA+PSBkaWZmX0JDIC0gbG93ZXJfYW5kX3VwcGVyX2ludGVydmFsX2xpbWl0MiAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVlX2RpZmZfQkMgPD0gZGlmZl9CQyArIGxvd2VyX2FuZF91cHBlcl9pbnRlcnZhbF9saW1pdDIsIDEsIDApLA0KICAgICAgICAgIHdpdGhpbl9wb2wgPSBpZl9lbHNlKHRydWVfZGlmZl9wb2wgPj0gZGlmZl9wb2wgLSBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydWVfZGlmZl9wb2wgPD0gZGlmZl9wb2wgKyBsb3dlcl9hbmRfdXBwZXJfaW50ZXJ2YWxfbGltaXQyLCAxLCAwKSwNCiAgICAgICAgICBjb21wYXJpc29uID0gcGFzdGUoeCwgeSwgc2VwID0gIiAtICIpDQogICApICU+JSANCiAgIGdyb3VwX2J5KGNvbXBhcmlzb24sIHNhbXBsZV9zaXplKSAlPiUgDQogICBzdW1tYXJpemUocGVyY193aXRoaW5fQkMgPSBzdW0od2l0aGluX0JDKSAvIHJlcGxpY2F0aW9uc19wZXJfc2V0dGluZyAqIDEwMCwNCiAgICAgICAgICAgICBwZXJjX3dpdGhpbl9wb2wgPSBzdW0od2l0aGluX3BvbCkgLyByZXBsaWNhdGlvbnNfcGVyX3NldHRpbmcgKiAxMDApICU+JSANCiAgIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoInBlcmMiKSwNCiAgICAgICAgICAgICAgICBuYW1lc190byA9ICJtZWFzdXJlIiwNCiAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicGVyY2VudGFnZSIsDQogICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gInBlcmNfd2l0aGluXyIpDQogICANCg0Kd2l0aGluX2ludGVydmFsX2RpZmZfZGlzdHJpYnV0aW9ucyAlPiUgZ2dwbG90KGFlcyhmYWN0b3Ioc2FtcGxlX3NpemUpLCBwZXJjZW50YWdlLCBjb2wgPSBmYWN0b3IoY29tcGFyaXNvbiksIGdyb3VwID0gZmFjdG9yKGNvbXBhcmlzb24pKSkrDQogICBnZW9tX3BvaW50KHNpemUgPSAyKSsNCiAgIGdlb21fbGluZShsaW5ld2lkdGggPSAxLjIpKw0KICAgZmFjZXRfd3JhcCh+bWVhc3VyZSkrDQogICB0aGVtZV9taW5pbWFsKCkrDQogICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpKw0KICAgbGFicyh5ID0gInBlcmNlbnRhZ2Ugb2Ygc2ltdWxhdGlvbnMgd2l0aGluIDAuMDI1IGludGVydmFsIG9mIHRydWUgc2NvcmUiLA0KICAgICAgICB4ID0gInNhbXBsZSBzaXplIiwNCiAgICAgICAgdGl0bGUgPSAiSW50ZXJ2YWwgb2YgKy0gMC4wMjUiLA0KICAgICAgICBjb2xvciA9ICJDb21wYXJpc29uIGJldHdlZW4iKSsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMyKSsNCiAgIHRoZW1lKGxlZ2VuZC5zcGFjaW5nID0gdW5pdCgwLjEsICJjbSIpLA0KICAgICAgICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLDAsMCwwKSwNCiAgICAgICAgIGxlZ2VuZC5ib3gubWFyZ2luID0gbWFyZ2luKDAsMCwwLDApLA0KICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYyguNSwuMDUpLA0KICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikNCmBgYA0KDQojIERpc2N1c3Npb24NCg0KQXMgb25lIGhhcyBleHBlY3RlZCwgdGhlICJuZWVkZWQiIHNhbXBsZSBzaXplIGluY3JlYXNlcyB0aGUgc3RyaWN0ZXIgd2Ugc2V0IHRoZSBpbnRlcnZhbHMgd2hpY2ggd2Ugd291bGQgYmUgc2F0aXNmaWVkIHdpdGguICANCg0KSWYgb25lIHdlcmUgdG8gb25seSBsb29rIGF0IHRoZSBwbG90cyB3aXRoIHRoZSBzdHJpY3RlciBpbnRlcnZhbDoNCg0KLSBPbiB0aGUgc2lkZSBvZiBiaW1vZGFsaXR5IGNvZWZmaWNpZW50LCBvbmUgY291bGQgYXJndWUgdG8gc2FtcGxlIGFyb3VuZCAxMjUwIHBhcnRpY2lwYW50cyBzbyB0aGF0IHdlIGhhdmUgYSBnb29kIGNoYW5jZSBvZiBhcm91bmQgODAlIHRvIGdldCBhIGdvb2QgZXN0aW1hdGUgZm9yIFsqKnJhcmUgZGlzdHJpYnV0aW9ucyoqXXtzdHlsZT0iY29sb3I6IGByIGNvbG9yc1szXWA7In0gKHNvIHRoYXQgaW4gY2FzZSB3ZSBkbyBzYW1wbGUgZnJvbSBhIHJhcmUgZGlzdHJpYnV0aW9uLCBpbiA4MCUgb2YgdGhlIHRpbWUsIHRoZSB0cnVlIGJpbW9kYWxpdHkgY29lZmZpY2llbnQgaXMgb2ZmIGJ5IG9ubHkgYSBzbWFsbCBhbW91bnQpLiBJZiBvbmUgd2FudHMgdG8gbWFrZSBzdXJlLCBldmVuIHNhbXBsaW5nIDIwMDAgcGFydGljaXBhbnRzIHdvdWxkIGJlIG5pY2UgdG8gaW5jcmVhc2UgdGhlIGNoYW5jZSB0byA5MCUsIGJ1dCBtYXliZSB0aGF0IGlzIHN0cmV0Y2hpbmcgdGhlIGJ1ZGdldCBhIGxpdHRsZSBiaXQuLi4NCg0KLSBGb3IgdGhlIG1lYXN1cmUgb2YgcG9sYXJpemF0aW9uLCBlc3RpbWF0aW5nIHRoZSB0cnVlIHNjb3JlIHZhbHVlIGlzIGhhcmRlc3QgaWYgb25lIGhhcyBhIFsqKnN0cm9uZ2x5IHBvbGFyaXplZCoqXXtzdHlsZT0iY29sb3I6IGByIGNvbG9yc1s0XWA7In0gZGlzdHJpYnV0aW9uLiBJbiB0aGlzIGNhc2UsIHdlIGhhdmUgYSA4MCUgY2hhbmNlIG9mIGdldHRpbmcgaXQgInJpZ2h0IiBpZiBvbmUgd291bGQgc2FtcGxlIDEyNTAgcGFydGljaXBhbnRzLCBvciA5MCUgZm9yIHNhbXBsaW5nIDIwMDAgcmVzcGVjdGl2ZWx5Lg0KDQpUaGUgbnVtYmVycyBoZXJlIGFyZSBvbmx5IHN1Z2dlc3Rpb25zLCBhbmQgYXJlIG9ubHkgYmFzZWQgb24gKipwcmVjaXNpb24qKiwgc28gdGhhdCB3ZSBjYW4gYmUgc3VyZSB0aGF0IHRoZSByZXN1bHRzIHdlIGdldCBhcmUgcmVsaWFibGUgZW5vdWdoLiBJbiBhbnkgY2FzZSwgdGhlc2UgbnVtYmVycyBhcmUgIm9ubHkiIHdvcnN0IGNhc2Ugc2NlbmFyaW9zLCBhcyB0aGUgb3RoZXIgcG9wdWxhdGlvbiBkaXN0cmlidXRpb25zIGFyZSBjb252ZXJnaW5nIGVhcmxpZXIuIFdlIGFyZSBhbHNvIG9ubHkgbG9va2luZyBhdCB0aGUgc3RyaWN0ZXIgc2NlbmFyaW8uLi4NCg0KV2hlbiBsb29raW5nIGZvciB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHR3byBkaXN0cmlidXRpb25zLCBvbmUgbmVlZCB0byBzYW1wbGUgMTUwMCBvciBldmVuIDI3NTAgcGVvcGxlIGZvciA4MCUgb3IgOTAlIGNoYW5jZSBvZiBiZWluZyBuZWFyIHRoZSB0cnVlIHNjb3JlLiAgDQoNCg0KIyMjIExpbWl0YXRpb25zDQoNCi0gRGlzY3Vzc2lvbiB3YXMgc29sZWx5IGJhc2VkIG9uIHRoZSBzdHJpY3RlciBpbnRlcnZhbCAoZS5nLiB0aGUgbGFzdCBwbG90KSwgbm90IG9uIHRoZSBsZW5pZW50IG9uZSB3aXRoIGEgYnJvYWRlciBpbnRlcnZhbC4NCi0gQXMgYWx3YXlzLCB0aGUgc2ltdWxhdGlvbiBzaG91bGQgYmUgdGFrZW4gd2l0aCBhIGdyYWluIG9mIHNhbHQsIGFzIHRoZXNlIHJlc3VsdHMgYXJlIG1hZGUgd2l0aCB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBkaXN0cmlidXRpb24gaXMgYXMgImJlYXV0aWZ1bCIgYXMgSSBtYWRlIGl0IGluIHRoZSBpbnRyb2R1Y3Rpb24gc2VjdGlvbi4uLiANCi0gV2hldGhlciB3ZSBjYW4gdHJ1bHkgc2FtcGxlIGFzIHJhbmRvbSBhcyB3ZSB3YW50IHRvLCBvciB3ZSBnZXQgc29tZSBzdWJzYW1wbGVzIGlzIG5vdCBvZiBkZWJhdGUgaGVyZS4gVGhpcyB3b3JrIGp1c3QgYXNzdW1lcyB0aGF0IHRoZSB3aG9sZSBwb3B1bGF0aW9uIGhhcyBYIGRpc3RyaWJ1dGlvbi4NCi0gTm8gY29tcGFyaXNvbiBiZXR3ZWVuIGRpZmZlcmVudCBzYW1wbGUgZGlzdHJpYnV0aW9ucyB3ZXJlIG1hZGUgKGUuZy4gb3ZlcmxhcCBjb2VmZmljaWVudCksIHNvIHdlIHN0aWxsIGhhdmUgbm8gaWRlYSBob3cgYmlnIHRoZSBzYW1wbGUgc2l6ZSBuZWVkcyB0byBiZSBpZiB3ZSB3YW50IHRvIGRpc2NvdmVyIGEgZGlmZmVyZW5jZSBvZiBYIGJldHdlZW4gdHdvIGNvdW50cmllcy8gZ3JvdXBzICoqaW4gY2FzZSB0aGVyZSBpcyBhIGRpZmZlcmVuY2UqKi4NCg0KIyBDcmVkaXRzDQoNCg0KIyMgUiBQYWNrYWdlcyBVc2VkDQoNCi0gYWdybXQgKFJ1ZWRpbiBEICgyMDIzKS4gX2Fncm10OiBDYWxjdWxhdGUgQ29uY2VudHJhdGlvbiBhbmQgRGlzcGVyc2lvbiBpbiBPcmRlcmVkIFJhdGluZyBTY2FsZXNfLiBSIHBhY2thZ2UgdmVyc2lvbiAxLjQyLjEyLA0KPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9YWdybXQ+LikgIA0KDQotIGRvUGFyYWxsZWwgKENvcnBvcmF0aW9uIE0sIFdlc3RvbiBTICgyMDIyKS4gX2RvUGFyYWxsZWw6IEZvcmVhY2ggUGFyYWxsZWwgQWRhcHRvciBmb3IgdGhlICdwYXJhbGxlbCcgUGFja2FnZV8uIFIgcGFja2FnZSB2ZXJzaW9uIDEuMC4xNywNCjxodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPWRvUGFyYWxsZWw+LikgIA0KDQotIGZvcmVhY2ggKE1pY3Jvc29mdCwgV2VzdG9uIFMgKDIwMjIpLiBfZm9yZWFjaDogUHJvdmlkZXMgRm9yZWFjaCBMb29waW5nIENvbnN0cnVjdF8uIFIgcGFja2FnZSB2ZXJzaW9uIDEuNS4yLCA8aHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1mb3JlYWNoPi4pICANCg0KLSBrbml0ciAoWGllIFkgKDIwMjMpLiBfa25pdHI6IEEgR2VuZXJhbC1QdXJwb3NlIFBhY2thZ2UgZm9yIER5bmFtaWMgUmVwb3J0IEdlbmVyYXRpb24gaW4gUl8uIFIgcGFja2FnZSB2ZXJzaW9uIDEuNDUsIDxodHRwczovL3lpaHVpLm9yZy9rbml0ci8+LikgIA0KDQotIHBzeWNoIChXaWxsaWFtIFJldmVsbGUgKDIwMjMpLiBfcHN5Y2g6IFByb2NlZHVyZXMgZm9yIFBzeWNob2xvZ2ljYWwsIFBzeWNob21ldHJpYywgYW5kIFBlcnNvbmFsaXR5IFJlc2VhcmNoXy4gTm9ydGh3ZXN0ZXJuIFVuaXZlcnNpdHksIEV2YW5zdG9uLCBJbGxpbm9pcy4gUg0KcGFja2FnZSB2ZXJzaW9uIDIuMy45LCA8aHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1wc3ljaD4uKSAgDQoNCi0gUkNvbG9yQnJld2VyIChOZXV3aXJ0aCBFICgyMDIyKS4gX1JDb2xvckJyZXdlcjogQ29sb3JCcmV3ZXIgUGFsZXR0ZXNfLiBSIHBhY2thZ2UgdmVyc2lvbiAxLjEtMywgPGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9UkNvbG9yQnJld2VyPi4pICANCg0KLSBybWFya2Rvd24gKEFsbGFpcmUgSiwgWGllIFksIERlcnZpZXV4IEMsIE1jUGhlcnNvbiBKLCBMdXJhc2NoaSBKLCBVc2hleSBLLCBBdGtpbnMgQSwgV2lja2hhbSBILCBDaGVuZyBKLCBDaGFuZyBXLCBJYW5ub25lIFIgKDIwMjMpLiBfcm1hcmtkb3duOiBEeW5hbWljDQpEb2N1bWVudHMgZm9yIFJfLiBSIHBhY2thZ2UgdmVyc2lvbiAyLjI1LCA8aHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duPi4pICANCg0KLSB0aWR5dmVyc2UgKFdpY2toYW0gSCwgQXZlcmljayBNLCBCcnlhbiBKLCBDaGFuZyBXLCBNY0dvd2FuIExELCBGcmFuw6dvaXMgUiwgR3JvbGVtdW5kIEcsIEhheWVzIEEsIEhlbnJ5IEwsIEhlc3RlciBKLCBLdWhuIE0sIFBlZGVyc2VuIFRMLCBNaWxsZXIgRSwgQmFjaGUgU00sDQpNw7xsbGVyIEssIE9vbXMgSiwgUm9iaW5zb24gRCwgU2VpZGVsIERQLCBTcGludSBWLCBUYWthaGFzaGkgSywgVmF1Z2hhbiBELCBXaWxrZSBDLCBXb28gSywgWXV0YW5pIEggKDIwMTkpLiDigJxXZWxjb21lIHRvIHRoZSB0aWR5dmVyc2Uu4oCdIF9Kb3VybmFsIG9mDQpPcGVuIFNvdXJjZSBTb2Z0d2FyZV8sICo0Kig0MyksIDE2ODYuIGRvaToxMC4yMTEwNS9qb3NzLjAxNjg2IDxodHRwczovL2RvaS5vcmcvMTAuMjExMDUvam9zcy4wMTY4Nj4uKSAgDQoNCi0gdmlzZGF0IChUaWVybmV5IE4gKDIwMTcpLiDigJx2aXNkYXQ6IFZpc3VhbGlzaW5nIFdob2xlIERhdGEgRnJhbWVzLuKAnSBfSk9TU18sICoyKigxNiksIDM1NS4gZG9pOjEwLjIxMTA1L2pvc3MuMDAzNTUgPGh0dHBzOi8vZG9pLm9yZy8xMC4yMTEwNS9qb3NzLjAwMzU1PiwNCjxodHRwOi8vZHguZG9pLm9yZy8xMC4yMTEwNS9qb3NzLjAwMzU1Pi4pICANCg==